# FILE: LocalSearchBroker.py
# DESC: obtains local business points in the proximity of LDCEntry object. 
#

import urllib
from datetime import date

from web.service.WebServiceBroker import WebServiceBroker
from web.service.DirectionBroker import DirectionBroker, G_TRAVEL_MODE_DRIVING, G_TRAVEL_MODE_WALKING
from lbc.models import LBCategory

from infra.logging import logger



LBP_MAX_DISTANCE = 2000


class LocalSearchBroker(WebServiceBroker):
	
	def __init__(self):
		self.searchHandlers_ = {}
		self.dirBroker_ = DirectionBroker()
			
		
	def getOrganizations(self, location, radius = LBP_MAX_DISTANCE):
		log = logger.getLogger()
		organizations = []
		
		try:
			log.debug('Searching for organizations near location = {%s} ...' % location)

			for lbcategory in LBCategory.objects.filter(active = True):
			
				log.debug('Searching for organizations belonging to lbcategory = {%s} ...' % lbcategory)
				
				if not self.searchHandlers_.has_key(lbcategory):
					self.searchHandlers_[lbcategory] = lbcategory.getSearchHandlers()

				for searchHandler in self.searchHandlers_[lbcategory]:
					organizations.extend(searchHandler.getOrganizations(location, radius, self.dirBroker_))
		
			return organizations

		except Exception, e:
			log.error('An exception has been raised, reason: %s' % e)
			raise e
		
		

	def getLBPoints(self, ldpoint, radius = LBP_MAX_DISTANCE):
		log = logger.getLogger()
		lbPoints = []
		
		try:
			log.debug('Searching for local business points near %s ...' % ldpoint)

			for lbCategory in LBCategory.objects.all():
			
				log.debug('Searching for %s ...' % lbCategory)

				for searchHandler in lbCategory.getSearchHandlers():
					lbPoints.extend(searchHandler.getLBPoints(ldpoint, radius, self.dirBroker_))
		
			return lbPoints

		except Exception, e:
			log.error('Failed to create local business points for ldPoint = %s, reason = %e' % (ldPoint, e))
			raise e

	def getLBPoints__(self, ldPoint, radius = LBP_MAX_DISTANCE):
		log = logger.getLogger()
		lbPoints = []
		dirBroker = DirectionBroker()

		try:
			log.debug('Searching for local business points near %s ...' % ldPoint)
			for lbCategory in LBCategory.objects.all():
				log.debug('Searching for %s ...' % lbCategory)
				
				if lbCategory.hasSearchModule():
					if not self.searchModules_.has_key(lbCategory.id):
						searchModule = lbCategory.getSearchModule()
						searchModule.fetch(lbCategory)
						self.searchModules_[lbCategory.id] = searchModule 

				for searchKeyword in lbCategory.getSearchKeywords():
					startIdx = 0
					moreResults = True
					response = None
					while moreResults:
						
						if self.searchModules_.has_key(lbCategory.id):
							searchModule = self.searchModules_[lbCategory.id]
							if self.searchModules_[lbCategory.id].searchDone():
								break
							response = searchModule.searchJSON(lbCategory, ldPoint, radius)
						else:
							response = self.requestJSON(GOOGLE_LOCAL_SEARCH_URL % (urllib.quote_plus(searchKeyword.encode('utf-8')), urllib.quote_plus(ldPoint.getLatLng()), startIdx))
							
						if response and response['responseStatus'] == G_GEO_SUCCESS:
							for lbpoint in response['responseData']['results']:
								(distance_c, duration_c) = dirBroker.getDistance(ldPoint.getLatLng(), '%s,%s' % (lbpoint['lat'], lbpoint['lng']), G_TRAVEL_MODE_DRIVING)
								if distance_c:
									if distance_c > radius:
										moreResults = False
										break
									(distance_w, duration_w) = dirBroker.getDistance(ldPoint.getLatLng(), '%s,%s' % (lbpoint['lat'], lbpoint['lng']), G_TRAVEL_MODE_WALKING)
									if distance_w:
										if distance_w > radius:
											moreResults = False
											break

										lbPoint = None
										lbPts = LBPoint.objects.filter(fnorg = lbpoint['titleNoFormatting']).filter(address = lbpoint['streetAddress'])
										if lbPts:
											lbPoint = lbPts[0]
										else:
											lbPoint = LBPoint()
										lbPoint.lbcategory = lbCategory
										lbPoint.fnorg = lbpoint['titleNoFormatting']
										lbPoint.address = lbpoint['streetAddress']
										lbPoint.city = lbpoint['city']
										lbPoint.lat = lbpoint['lat']
										lbPoint.lng = lbpoint['lng']
										lbPoint.accuracy = lbpoint['accuracy']
										lbPoint.last_fetch = date.today()
							
										lbPoint.save()
							
										log.debug('Found %s' % lbPoint)
								
							
										lbPoints.append((lbPoint,(distance_c, duration_c, distance_w, duration_w)))
										
							# REVISIT: simplify logic
							if len(response['responseData']['results']) > 0 and moreResults > 0 and not self.searchModules_.has_key(lbCategory.id):
								startIdx += 8
							else:
								moreResults = False
						else:
							moreResults = False
				
			return lbPoints
		
		except Exception, e:
			log.error('Failed to create local business points for ldPoint = %s, reason = %e' % (ldPoint, e))
			raise e
